﻿#region LicenseHeader

// Copyright 2012 The Trustees of Indiana University.  All rights reserved.
// 
// The Indiana University licenses this file to you under the Apache License, 
// Version 2.0 (the "License"); you may not use this file except in compliance 
// with the License.  You may obtain a copy of the License at 
// 
// http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software 
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
// See the License for the specific language governing permissions and 
// limitations under the License.
// 
// @author Thilina Gunarathne (tgunarat@indiana.edu)

#endregion

using System;
using System.IO;
using System.Linq;
using NAligner;

namespace SeqAligner
{
    public class Aligner
    {
        private static float _gapOpenPenalty = 14.0f;
        private static float _gapExtensionPenalty = 4.0f;


        public static void WriteToBinaryFlile(short[][] data, int x, int y, string file, bool transpose)
        {
            FileStream stream = new FileStream(file, FileMode.Create);
            BinaryWriter bw = new BinaryWriter(stream);
            if (transpose)
            {
                bw.Write(y);
                bw.Write(x);
            }
            else
            {
                bw.Write(x);
                bw.Write(y);
            }
            for (int i = 0; i < x; i++)
            {
                for (int j = 0; j < y; j++)
                {
                    if (transpose)
                    {
                        bw.Write(data[j][i]);
                    }
                    else
                    {
                        bw.Write(data[i][j]);
                    }
                }
            }

            stream.Close();
        }

        public static void WriteDataToFiles(short[][] data, int x, int y, string file1, string file2)
        {
            WriteToBinaryFlile(data, x, y, file1, false);
            if (!file1.Trim().Equals(file2.Trim()))
            {
                WriteToBinaryFlile(data, x, y, file2, true);
            }
        }

        private static void Main(string[] args)
        {
            int xStart, xEnd, yStart, yEnd;
            string inputFile, outputFile1, outputFile2;
            // args = new string[] { "0", "2", "100", "200", "Y_human_chimp.txt", "outputfile1", "outputfile2" };

            if (args.Length != 7)
            {
                Console.WriteLine("Usage: Aligner xStart xEnd yStart yEnd inputFile outputFile1  outputFile2");
                return;
            }

            xStart = Int32.Parse(args[0]);
            xEnd = Int32.Parse(args[1]);
            yStart = Int32.Parse(args[2]);
            yEnd = Int32.Parse(args[3]);
            inputFile = args[4];
            outputFile1 = args[5];
            outputFile2 = args[6];
            bool writeFile = Boolean.Parse(args[7]);

            align(xStart, xEnd, yStart, yEnd, inputFile, outputFile1, outputFile2, writeFile);
        }

        public static void align(int xStart, int xEnd, int yStart, int yEnd, string inputFile, string outputFile1,
                                 string outputFile2, bool doWriteFile)
        {
            FastaScanner scanner = new FastaScanner(inputFile);
            FastaParser parser = new FastaParser(scanner);
            parser.Parse();

            int seqCount = parser.Sequences.Count;
            //Stopwatch alignTimer = Stopwatch.StartNew();

            int xLen = (xEnd - xStart) + 1;
            int yLen = (yEnd - yStart) + 1;

            short[][] distances = new short[xLen][];
            for (int i = 0; i < xLen; i++)
            {
                distances[i] = new short[yLen];
            }
            for (int i = xStart; i <= xEnd; i++)
            {
                for (int j = yStart; j <= yEnd; j++)
                {
                    distances[i - xStart][j - yStart] =
                        (short) (PerformPairwiseAlignment(parser.Sequences[i], parser.Sequences[j])*short.MaxValue);
                }
            }

            if (doWriteFile)
            {
                WriteDataToFiles(distances, xLen, yLen, outputFile1, outputFile2);
            }

            //alignTimer.Stop();
            //long loopTime = alignTimer.ElapsedMilliseconds;
            //Console.Out.WriteLine("First Loop Time :" + loopTime);         
        }

        public static void WriteDataToBinaryFile(BinaryWriter bw, int index, float[] distances)
        {
            int size = distances.Count();
            bw.Write(index);
            for (int i = 0; i < size; i++)
            {
                bw.Write((short) (distances[i]*short.MaxValue));
            }
        }

        public static void WrtieDataToTextFile(TextWriter tw, int index, float[] distances)
        {
            int size = distances.Count();
            tw.Write(index + " ");
            for (int i = 0; i < size; i++)
            {
                tw.Write(distances[i] + " ");
            }
            tw.WriteLine();
        }

        public static float PerformPairwiseAlignment(Sequence seqA, Sequence seqB)
        {
            SmithWatermanGotoh aligner = new SmithWatermanGotoh();
            PairwiseAlignment alignment = aligner.AlignDNA(seqA, seqB, _gapOpenPenalty, _gapExtensionPenalty);
            return (1.0f - alignment.PercentIdentity);
            // PairResult result = new PairResult(alignment);
            // return result;
        }
    }
}